curl --request GET \
--url https://api.tetrafi.io/api/v1/prices/stream \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.tetrafi.io/api/v1/prices/stream"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.tetrafi.io/api/v1/prices/stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tetrafi.io/api/v1/prices/stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.tetrafi.io/api/v1/prices/stream"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.tetrafi.io/api/v1/prices/stream")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tetrafi.io/api/v1/prices/stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"buy": "USDC",
"computedAt": "2023-11-07T05:31:56Z",
"referenceMid": {
"buyPerSell": "<string>",
"buyUsd": "<string>",
"sellUsd": "<string>"
},
"rows": [
{
"status": "priced",
"firm": true,
"lastLook": true,
"basis": "liveQuote",
"blockNumber": 1,
"buyVariant": {
"kind": "native",
"name": "<string>"
},
"ladderSemantics": "incremental",
"levels": [
{
"price": "10000",
"size": "10000"
}
],
"location": {
"chain": "eip155:8453",
"kind": "singleChain"
},
"observedAt": "2023-11-07T05:31:56Z",
"sellVariant": {
"kind": "native",
"name": "<string>"
},
"sizes": [
{
"buy": "10000",
"fees": [
{
"amount": "10000",
"asset": "USDC",
"chain": "eip155:8453",
"included": true,
"kind": "provider",
"leg": 1,
"paidBy": "taker",
"bound": "maximum"
}
],
"filled": "full",
"netReceived": {
"amount": "10000",
"kind": "known"
},
"sell": "10000",
"feeObservedAt": "2023-11-07T05:31:56Z"
}
],
"spreadFromMidBps": {
"bps": 123,
"kind": "known"
},
"staleAfter": "2023-11-07T05:31:56Z",
"venue": {
"id": "<string>",
"logoUrl": "<string>",
"modality": "amm",
"name": "<string>",
"protocolVersion": "<string>",
"integrationMode": "basic",
"venueId": "<string>",
"venueName": "<string>"
}
}
],
"sell": "USDC",
"sellBalances": [
{
"amount": "10000",
"chain": "<string>",
"observedAt": "2023-11-07T05:31:56Z",
"state": "fresh"
}
],
"referenceMidUnavailable": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}`GET /api/v1/prices/stream`: `snapshot` events per pair on change.
curl --request GET \
--url https://api.tetrafi.io/api/v1/prices/stream \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.tetrafi.io/api/v1/prices/stream"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.tetrafi.io/api/v1/prices/stream', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.tetrafi.io/api/v1/prices/stream",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.tetrafi.io/api/v1/prices/stream"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.tetrafi.io/api/v1/prices/stream")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.tetrafi.io/api/v1/prices/stream")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"buy": "USDC",
"computedAt": "2023-11-07T05:31:56Z",
"referenceMid": {
"buyPerSell": "<string>",
"buyUsd": "<string>",
"sellUsd": "<string>"
},
"rows": [
{
"status": "priced",
"firm": true,
"lastLook": true,
"basis": "liveQuote",
"blockNumber": 1,
"buyVariant": {
"kind": "native",
"name": "<string>"
},
"ladderSemantics": "incremental",
"levels": [
{
"price": "10000",
"size": "10000"
}
],
"location": {
"chain": "eip155:8453",
"kind": "singleChain"
},
"observedAt": "2023-11-07T05:31:56Z",
"sellVariant": {
"kind": "native",
"name": "<string>"
},
"sizes": [
{
"buy": "10000",
"fees": [
{
"amount": "10000",
"asset": "USDC",
"chain": "eip155:8453",
"included": true,
"kind": "provider",
"leg": 1,
"paidBy": "taker",
"bound": "maximum"
}
],
"filled": "full",
"netReceived": {
"amount": "10000",
"kind": "known"
},
"sell": "10000",
"feeObservedAt": "2023-11-07T05:31:56Z"
}
],
"spreadFromMidBps": {
"bps": 123,
"kind": "known"
},
"staleAfter": "2023-11-07T05:31:56Z",
"venue": {
"id": "<string>",
"logoUrl": "<string>",
"modality": "amm",
"name": "<string>",
"protocolVersion": "<string>",
"integrationMode": "basic",
"venueId": "<string>",
"venueName": "<string>"
}
}
],
"sell": "USDC",
"sellBalances": [
{
"amount": "10000",
"chain": "<string>",
"observedAt": "2023-11-07T05:31:56Z",
"state": "fresh"
}
],
"referenceMidUnavailable": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}{
"docUrl": "<string>",
"error": "<string>",
"message": "<string>",
"retryable": true,
"timestamp": 123,
"details": {
"capabilityBlockers": [
{
"code": "WORKSPACE_NOT_FOUND",
"estimate": "minutes",
"message": "<string>",
"unlockStep": "finish_onboarding",
"currentlyDue": [
"<string>"
]
}
],
"conflicts": [
"<string>"
],
"field": "<string>",
"reason": "<string>",
"remediation": "<string>",
"retryAfter": "PT120S",
"value": "<string>"
},
"next": {
"kind": "order"
},
"requestId": "<string>"
}Authorizations
Send the API key in X-API-Key.
Request signing. A key with signing enabled (POST /api/v1/workspaces/{workspaceId}/users/me/api-keys/{keyId}/signing, which returns the signing secret once) must also sign every request:
X-Timestamp: the current Unix time in seconds, within 300 seconds of the server clock.X-Signature: lower-case hex HMAC-SHA256 of the canonical string, keyed with the signing secret's 32 bytes (the secret is 64 hex characters; decode it first).
The canonical string is four lines joined by \n, with no trailing newline: the X-Timestamp value, the upper-case method, the path with its query exactly as sent (/api/v1/orders?limit=1), and the lower-case hex SHA-256 of the exact body bytes (the SHA-256 of an empty body when there is none).
A write's signature (POST, PUT, PATCH, DELETE) is accepted once: sending the same signed write again is refused, so a retry signs again. A read (GET, HEAD) changes nothing, so an identical signed read inside the window is admitted again and polling needs no nonce.
A signing refusal is a 401 AUTH_REQUIRED whose details.reason names the cause: SIGNATURE_REQUIRED (a header is missing), SIGNATURE_INVALID (a malformed header or a wrong signature), TIMESTAMP_OUT_OF_RANGE or SIGNATURE_REPLAYED.
Headers
The workspace the request acts in. An API key bound to a workspace implies it: omit the header, or send that same id (another id answers 403 WORKSPACE_SCOPE_MISMATCH). A member session must send it (400 MISSING_WORKSPACE without it).
Query Parameters
1 to 25 comma-separated SELL:BUY Asset pairs (USDC:USDT,USDC:ZAR)
As GET /prices, applied to every pair
As GET /prices, applied to every pair
1 to 8 comma-separated positive sell amounts, applied to every pair; pairs × sizes at most 125
CAIP-2 pin for the sell side
CAIP-2 pin for the buy side
Comma-separated venue ids to keep
Comma-separated venue ids to drop
any | nativeOnly
any | nativeOnly
Comma-separated singleChain, crossChain, offChain
Comma-separated amm, solver, bridge, rfq, orderBook, fiat
Response
text/event-stream: a snapshot event (a GET /prices body for one pair) when that pair's rows change, a keep-alive comment every 15 s; the stream ends after 10 minutes
GET /prices 200, and each snapshot event of GET /prices/stream.
A registry Asset id (USDC, USDe). Matched case-insensitively against the catalogue,
which answers it in the registry's own spelling.
"USDC"
When the server built this snapshot (it reuses one for about a second).
The pair's indicative reference mid, from the same source as POST /quotes. null only when the read failed, with the cause in referenceMidUnavailable; never zero, stale or guessed. Never a quote input.
Show child attributes
Show child attributes
Sorted like quotes: netReceived at the first size descending, an unavailable value
last, then by venue id (there is no best).
One venue's price for one location and variant pair.
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
- Option 7
- Option 8
Show child attributes
Show child attributes
A registry Asset id (USDC, USDe). Matched case-insensitively against the catalogue,
which answers it in the registry's own spelling.
"USDC"
The sell Asset in the caller's own Default-binding wallets, one memory-only read
per deployment (only the pinned chain when sell.chain is set). Display only, never a
quote input. Empty for an API key, before the first warm, or with no bound wallet:
unknown, never zero. A client totals the fresh entries and names every other entry
as not read, never a silent partial total.
One chain's read of the sell Asset in the caller's Default-binding wallet.
New values may be added; treat unknown values as other.
- Option 1
- Option 2
Show child attributes
Show child attributes
Why referenceMid is null: marketDataUnavailable (a market-data refusal or outage),
notPriced (no reference price exists for an asset) or timedOut (past the read's budget).